package com.leenmvc.core.dao.sqlServer.wrapper;

import com.leenmvc.core.annotation.FillField;
import com.leenmvc.core.base.BaseEntity;
import com.leenmvc.core.dao.communal.Wrapper;
import com.leenmvc.core.enums.EntityInfoEnum;
import com.leenmvc.core.utils.BeanUtils;
import com.leenmvc.core.utils.ClassUtil;
import com.leenmvc.core.utils.ConvertUtils;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.collection.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 拼装复杂sql语句的类
 */
public class SqlServerQueryWrapper extends Wrapper {

    private final static Logger logger = LoggerFactory.getLogger(SqlServerQueryWrapper.class);

    public SqlServerQueryWrapper() {
    }

    public SqlServerQueryWrapper(Class<?> cls) {
        this.cls = cls;
        this.columns = ClassUtil.getColumns(cls);
    }

    @Override
    public SqlServerQueryWrapper addSet(String setKey, Object value) {
        return null;
    }

    /**
     * 该方法获取SQL语句
     *
     * @return
     */
    @Override
    public String getSql() {
        sql.setLength(0);
        String simpleName = cls.getSimpleName();
        Field[] fields = BaseEntity.classInfo.get(cls).getFieldArray(EntityInfoEnum.FILL_FIELD);
        Set<String> inFields = new HashSet<>();
        if (fields.length > 0) {
            /** 处理有@fillField注解的sql语句
             select a.id, a.role_id, a.menu_id from sys_role_menu a
             inner join sys_role b on a.role_id = b.id
             inner join sys_menu c on a.menu_id = c.id
             where a.id = '123456'
             */
            sql.insert(0, "select " + StringUtils.join(columns, ","));

            int sqlLen = sql.length();
            sql.append(" from " + BaseEntity.getTableName(cls));
            sql.append(" " + simpleName);
            String[] outClsFields = new String[0];
            for (Field field : fields) {
                FillField annoFillField = field.getAnnotation(FillField.class);
                Class<?> outCls = annoFillField.entity();
                String outField = BaseEntity.humpLine(annoFillField.outField());
                String inField = annoFillField.inField();
                String outName = outCls.getSimpleName();
                if (!inFields.contains(inField)) {
                    sql.append(" left join ");
                    sql.append(BaseEntity.getTableName(outCls));
                    sql.append(" " + outName);
                    sql.append(" on " + simpleName + "." + BaseEntity.humpLine(inField) + " = " + outName + "." + BaseEntity.getPrimaryColumnName(outCls));
                }
                outClsFields = ArrayUtils.arrayAdd(outClsFields, outName + "." + outField + " " + BaseEntity.humpLine(field.getName()));
                inFields.add(inField);
            }
            sql.insert(sqlLen, ", " + StringUtils.join(outClsFields, ","));
        } else {
            sql.insert(0, "select " + StringUtils.join(columns, ","));
            sql.append(" from " + BaseEntity.getTableName(cls));
            sql.append(" " + simpleName);
        }
        if (where.length() > 0) {
            sql.append(" where " + where);
        }
        if (orderBy.length() > 0) {
            sql.append(" order by " + orderBy);
        }
        logger.info("wrap类创建Sql语句，生成的SQL语句为：{}", sql);
        return sql.toString();
    }

    @Override
    public Wrapper and() {
        this.where.append(" and ");
        return this;
    }

    @Override
    public Wrapper joinOne(String field) {
        return this;
    }

    @Override
    public Wrapper joinMul(String field) {
        return this;
    }

    @Override
    public Wrapper joinField(String field) {
        return null;
    }

    public Wrapper and(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        this.where.append(" and ").append(field + " = ?");
        paramList.add(value);
        conditons.add(BaseEntity.humpLine(field) + " = ?");
        return this;
    }

    // or 连接符
    public Wrapper or() {
        this.where.append(" or ");
        return this;
    }

    // 使用 select * from user where name like '%jen%' or age > 20  and (sex = 2 and sc = 9)
    public Wrapper linkOr() {
        int index = this.where.lastIndexOf(conditons.get(conditons.size() - 1), this.where.length() - 1);
        this.where.insert(index, "(");
        this.where.append(" or)");
        return this;
    }

    // 如果传参数那么默认就是用等号连接
    public Wrapper or(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        this.where.append(" or ").append(field + " = ?");
        paramList.add(value);
        conditons.add(BaseEntity.humpLine(field) + " = ?");
        return this;
    }

    public SqlServerQueryWrapper count(Class<?> cls) {
        this.cls = cls;
        this.sql.append("select count(1) from ").append(BaseEntity.getTableName(cls));
        return this;
    }

    // order by
    public SqlServerQueryWrapper orderBy(String field, String desc) {
        this.orderBy.setLength(0);
        this.orderBy.append(BaseEntity.humpLine(field)).append(" ").append(desc);
        return this;
    }

    /**
     * 排除某个字段
     *
     * @param fields
     * @return
     */
    public SqlServerQueryWrapper decField(String... fields) {
        for (String field : fields) {
            field = BaseEntity.humpLine(field);
            this.columns.remove(field);
        }
        return this;
    }

    /**
     * 只显示某些字段
     *
     * @param fields
     * @return
     */
    public SqlServerQueryWrapper incField(String... fields) {
        this.columns.clear();
        for (String field : fields) {
            field = BaseEntity.humpLine(field);
            this.columns.add(field);
        }
        return this;
    }

    /**
     * 限制条数
     *
     * @param pageInfo
     * @return
     */
    public SqlServerQueryWrapper limit(Integer...pageInfo) {
        return this;
    }

    /**
     * 限制page
     *
     * @param page
     * @return
     */
    public SqlServerQueryWrapper limitPage(int page) {
        this.limit[0] = page;
        return this;
    }

    /**
     * 限制page
     *
     * @param pageSize
     * @return
     */
    public SqlServerQueryWrapper limitSize(int pageSize) {
        this.limit[1] = pageSize;
        return this;
    }


    /**
     * 等于
     */
    public SqlServerQueryWrapper eq(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " = ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field + " = ?");
            conditons.add(BaseEntity.humpLine(field) + " = ?");
        }
        paramList.add(value);
        return this;
    }

    /**
     * 不等于
     *
     * @param field
     * @param value
     * @return
     */
    public SqlServerQueryWrapper uEq(String field, Object value) {
//        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " != ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" != ").append("?");
            conditons.add(field + " != ?");
        }
        paramList.add(value);
        return this;
    }

    /**
     * 右边随意
     *
     * @param field
     * @param value
     * @return
     */
    public SqlServerQueryWrapper likeR(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '" + value + "%'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like '").append(value).append("%'");
            conditons.add(field + " like '" + value + "%'");
        }
        return this;
    }

    /**
     * 左边随意
     *
     * @param field
     * @param value
     * @return
     */
    public SqlServerQueryWrapper likeL(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '%" + value + "'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like '%").append(value).append("'");
            conditons.add(field + " like '%" + value + "'");
        }
        return this;
    }

    /**
     * 全like
     *
     * @param field
     * @param value
     * @return
     */
    public SqlServerQueryWrapper like(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        value = value.toString().replaceAll("([%'])", "\\\\$1");
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " like '%" + value + "%'");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" like ").append("'%").append(value).append("%'");
        }
        return this;
    }

    /**
     * 根据数组中
     *
     * @param field
     * @param ins
     * @return
     */
    public SqlServerQueryWrapper in(String field, Object[] ins) {
        if (ins == null) return this;
        setAnd();
        if (ins.length == 0) {
            ins = ArrayUtils.arrayAdd(ins, "999999999999999999999");
        }
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " in (" + StringUtils.join(ins, ",") + ")");
            int last = this.where.lastIndexOf("(");
            last = this.where.lastIndexOf("(", last - 1);
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" in (" + StringUtils.join(ins, ",") + ")");
            conditons.add(field + " in (" + StringUtils.join(ins, ",") + ")");
        }
        return this;
    }

    public <T> SqlServerQueryWrapper in(String field, List<T> ins) {
        return in(field, ins.toArray());
    }

    // between
    public SqlServerQueryWrapper between(String field, Object start, Object end) {
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " between " + start + " and " + end);
            int last = this.where.lastIndexOf("(");
            last = this.where.lastIndexOf("(", last - 1);
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" between ").append(start).append(" and ").append(end);
            conditons.add(field + " between " + start + " and " + end);
        }
        return this;
    }

    // <
    public SqlServerQueryWrapper lt(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " < ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" < ?");
            conditons.add(field + " < ?");
        }
        paramList.add(value);
        return this;
    }

    // >
    public SqlServerQueryWrapper gt(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " > ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" > ?");
            conditons.add(field + " > ?");
        }
        paramList.add(value);
        return this;
    }

    // >=
    public SqlServerQueryWrapper ge(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " >= ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" >= ?");
            conditons.add(field + " >= ?");
        }
        paramList.add(value);
        return this;
    }

    // <=
    public SqlServerQueryWrapper le(String field, Object value) {
        if (StringUtils.isEmpty(value)) return this;
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " <= ?");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" <= ?");
            conditons.add(field + " <= ?");
        }
        paramList.add(value);
        return this;
    }

    // is null
    public SqlServerQueryWrapper isNull(String field) {
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " is null");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" is null ");
        }
        return this;
    }

    // is not null
    public SqlServerQueryWrapper isNotNull(String field) {
        setAnd();
        field = BaseEntity.humpLine(field);
        if (StringUtils.endsWithIgnoreCase(this.where, "or)")) {
            this.where.insert(this.where.length() - 1, " " + field + " is not null");
            int last = this.where.lastIndexOf("(");
            conditons.add(this.where.substring(last));
        } else {
            this.where.append(field).append(" is not null");
        }
        return this;
    }

    /**
     * 将hashMap转成QueryWrapper
     * like关键字 规则如下（前缀）：
     * LL_  左像（%在左边）
     * RL_  右像 (%在右边)
     * AL_  全像 (%在两边)  AL_username   %leen%
     * orderBY关键字规则如下 （前缀）：
     * OB_  排序的字段  value值为DESC 或 ESC    OB_createTime   DESC
     * page, pageSize 特殊字保留字 （只做分页功能）
     *
     * @return
     */
    public SqlServerQueryWrapper mapToWrapper(Map<String, Object> params) {
        a:
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            String[] overcomes = new String[]{"_t", "limit", "page", "pageSize", "order", "orderField", "limit"};
            String[] valueArr = entry.getKey().split("_");
            switch (valueArr[0]) {
                case "LL":
                    this.likeL(valueArr[1], entry.getValue());
                    break;
                case "RL":
                    this.likeR(valueArr[1], entry.getValue());
                    break;
                case "AL":
                    this.like(valueArr[1], entry.getValue());
                    break;
                case "OB":
                    if ("ESC".equals(entry.getValue())) {
                        this.orderBy(valueArr[1], " ");
                    } else {
                        this.orderBy(valueArr[1], entry.getValue().toString());
                    }
                    break;
                case "page":
                    this.limitPage((int) ConvertUtils.convertType(entry.getValue(), Integer.class));
                    break;
                case "pageSize":
                    this.limitSize((int) ConvertUtils.convertType(entry.getValue(), Integer.class));
                    break;
                default:
                    b:
                    for (String over : overcomes) {
                        if (over.equals(entry.getKey())) {
                            continue a;
                        }
                    }
                    if (this.fields.indexOf(entry.getKey()) != -1) {
                        this.eq(entry.getKey(), entry.getValue());
                    }
            }
        }
        return this;
    }

    @Override
    public void copy(Wrapper wrapper) {
        this.where = BeanUtils.deepClone(wrapper.getWhere());
        this.paramList = BeanUtils.deepClone(wrapper.getParamList());
        this.limit = BeanUtils.deepClone(wrapper.getLimit());
    }
}